The C Language Interface

This chapter describes the facility of KCL to interface the C language and KCL. With this facility, the user can arrange his or her C-language programs so that they can be invoked from KCL. In addition, the user can write Lisp function definitions in the C language to increase runtime efficiency.


The basic idea of interfacing the C language is this: As mentioned in Chapter 6, the KCL compiler, given a Lisp source file, creates an intermediate C-language program file, called c-file, which is then compiled by the C-language compiler to obtain the final fasl-file. Usually, the c-file consists of C-language function definitions. The first C-language function in the c-file is the ``initializer'', which is executed when the fasl file is loaded, and the other C-language functions are the C versions of the Lisp functions (including macro expansion functions) defined in the source file. By using the top-level macros Clines and defCfun described below, the user can direct the compiler to insert his or her own C-language function definitions and/or C-language preprocessor macros such as #define and #include into the c-file. In order that such C-language functions be invoked from KCL, another top-level macro defentry is used. This macro defines a Lisp function whose body consists of the calling sequence to the specified C-language function.


The C-language function definitions are placed in the c-file in the order of the corresponding Lisp functions defined in the source file. That is, the C code for the first Lisp function comes first, the C code for the second Lisp function comes second, and so on. If a Clines or defCfun macro form appears between two Lisp function definitions in the source file, then the C code specified by the macro is placed in between the C code for the Lisp functions.


We define some terminology here which is used throughout this Chapter. A C-id is either a Lisp string consisting of a valid C-language identifier, or a Lisp symbol whose print-name, with all its alphabetic characters turned into lower case, is a valid C identifier. Thus the symbol foo is equivalent to the string ``foo'' when used as a C-id. Similarly, a C-expr is a string or a symbol that may be regarded as a C-language expression. A C-type is one of the Lisp symbols int, char, float, double, and object. Each corresponds to a data type in the C language; object is the type of Lisp object and other C-types are primitive data types in the C language.



Clines {string}*[Macro]

When the KCL compiler encounters a macro form (Clines string1 ... stringn), it simply outputs the strings into the c-file. The arguments are not evaluated and each argument must be a string. Each string may consist of any number of lines, and separate lines in the string are placed in separate lines in the c-file. In addition, each string opens a fresh line in the c-file, i.e., the first character in the string is placed at the first column of a line. Therefore, C-language preprocessor commands such as #define and #include will be recognized as such by the C compiler, if the ' # ' sign appears as the first character of the string or as the first character of a line within the string.

In order to clearly distinguish C code from other parts of Lisp programs, we, the implementors of KCL, make it our rule to start each C code line with a percent sign ' % '. We define % as a read macro which returns the rest of the line as a string. For example,

        ;;; C version of TAK.
        (Clines

        %       int tak(x, y, z)
        %       int x, y, z;
        %       {       if (y >= x) return(z);
        %               else return(tak(tak(x-1, y, z),
        %                               tak(y-1, z, x),
        %                               tak(z-1, x, y)));
        %       }
        )

Of course, the user may instead enclose each C code line or the whole C code with double quotes, but we recommend the use of the percent sign read macro. Since the percent sign read macro is not a standard read macro, the users must define this read macro by themselves. We use the following definition.

 
      (set-macro-character
          #\\%
          #'(lambda (stream char) (values (read-line stream)))))

Here, the lambda-expression returns the first value of read-line by using values as a filter.

When interpreted, a Clines macro form expands to nil.

defentry function parameter-list C-function[Macro]

defentry defines a Lisp function whose body consists of the calling sequence to a C-language function. function is the name of the Lisp function to be defined, and C-function specifies the C function to be invoked. C-function must be either a list (type C-id) or C-id, where type and C-id are the type and the name of the C function. type must be a C-type or the symbol void which means that the C function returns no value. (object C-id ) may be abbreviated as C-id. parameter-list is a list of C-types for the parameters of the C function. For example, the following defentry form defines a Lisp function tak from which the C function tak above is called.

	(defentry tak (int int int) (int tak))

The Lisp function tak defined by this defentry form requires three arguments. The arguments are converted to int values before they are passed to the C function. On return from the C function, the returned int value is converted to a Lisp integer (actually a fixnum) and this fixnum will be returned as the value of the Lisp function. See below for type conversion between Lisp and the C language.

A defentry form is treated in the above way only when it appears as a top-level form of a Lisp source file. Otherwise, a defentry form expands to nil.

defla name lambda-list {declaration | doc-string}* {form}*[Macro]

When interpreted, defla is exactly the same as defun. That is, (defla name lambda-list . body) expands to (defun name lambda-list . body) . However, defla forms are completely ignored by the compiler; no C-language code will be generated for defla forms. The primary use of defla is to define a Lisp function in two ways within a single Lisp source file; one in the C language and the other in Lisp. defla is short for DEFine Lisp Alternative.

Suppose you have a Lisp source file whose contents are:

 
      ;;; C version of TAK.
      (Clines

      %      int tak(x, y, z)
      %      int x, y, z;
      %      {       if (y >= x) return(z);
      %              else return(tak(tak(x-1, y, z),
      %                              tak(y-1, z, x),
      %                              tak(z-1, x, y)));
      %      }
      
      )
 
      ;;;  TAK calls the C function tak defined above.
      (defentry tak (int int int) (int tak))
      ;;;  The alternative Lisp definition of TAK.
      (defla tak (x y z)
         (if (>= y x)
             z
             (tak (tak (1- x) y z)
                  (tak (1- y) z x)
                  (tak (1- z) x y))))

When this file is loaded into KCL, the interpreter uses the Lisp version of the tak definition. Once this file has been compiled, and when the generated fasl file is loaded into KCL, a function call to tak is actually the call to the C version of tak.

defCfun header n {element}*[Macro]

defCfun defines a C-language function which calls Lisp functions and/or which handles Lisp objects. header is a string consisting of the C code for

the optional type-specifier of the C function,
the function-declarator of the C function, and
the type-decl-list of the parameters to the C function.

(For the C-language terminology, refer to The C Programming Language by Brian W. Kernighan and Dennis M. Ritchie.) The rest of the C function definition, i.e., the function-statement, is given by elements. Each element may be a string, in which case the string is treated in the same way as the arguments to the Clines macro. Or else, the element is a list ((name arg1 ... argn ) place1 ... placem ) . The compiler translates this list into a calling sequence to the Lisp function whose name is name. As will be mentioned later, name may be quote, but name may not be the name of any other special form or a macro. The args specify the arguments to the function and the places specify where the values should go. Thus the list-formed element could be regarded as something like the Lisp form:

 
            (multiple-value-setq
                ( place1  ...  placem )
                ( name   arg1  ...  argn )).

Each arg is a list (C-type C-expr), where C-expr is any C-language expression of the type C-type. If type is object, then arg may be written simply as C-expr. Similarly, each place is a list (C-type C-expr), or it may be abbreviated as C-expr if C-type is object. The C-expr in this case is any lvalue (in the terminology of the C language), i.e., it may be any valid C-language code that can be written at the left side of an assignment.

The function call is performed as follows. The args are evaluated, and the values are sent to the specified Lisp function after type conversion from C to Lisp. On return from the called Lisp function, each returned value is assigned to the corresponding place, i.e., the first returned value goes to place1, the second to place2, and so on. If there are more places than the values returned, extra values of nil are assigned to the remaining places. If there are more values than places, the excess values are simply discarded. If necessary, Lisp-to-C type conversion may take place before each returned value is assigned.

If the Lisp function is called just for side-effects, then the list-formed element may be abbreviated as a one-level list (name arg1 ... argn).

As a special case, if a list-formed element is of the form ((quote value) place), the Lisp object value is assigned to place. Here value may be any Lisp object.

The following defCfun form defines the C function silly which adds 100 to the value of the parameter x and prints the result in three different ways. The second argument to defCfun will be described later, and the user may ignore it.

        (defCfun ``silly(x) int x;'' 0
        %       int y;
                ((+ (int x) (int ``100'')) (int y))
        %       printf(``\\n%d'', y);
        %       y = x+100;
                (print (int y))
                (print (int ``x+100''))
         )

When a C function handles Lisp objects (i.e., data of type object), the user should be careful enough so that the objects may not be garbage-collected. This is because the garbage collector of KCL does not take care of Lisp objects used in the C function. See the following C function which is assumed to return a two-element list consisting of its two arguments.

 
        (defCfun ``object list2(x,y) object x,y;'' 0
        %      object z;
               ('nil z)
               ((cons y z) z)
               ((cons x z) z)
         %     return(z);
         )

When invoked, list2 first sets nil to the variable z, conses y to z, and then conses x. Each time cons is called, a new cons cell is allocated and the pointer to this cell is stored in z. However, there is no way to inform the garbage collector that the cells are referenced from the C variable z. Suppose that the cons cell allocated by the first cons is the last cons cell available at that time. Then, during execution of the second call to cons, the garbage collector begins to run and, unfortunately, the cons cell in z will be destroyed so that the cell can be recycled for further use.

To prevent a Lisp object from being unexpectedly garbage collected, the user must save the object in some place that is recognized by the garbage collector. The second parameter n to defCfun is used to reserve n such places for each call to the C function. In the body of the C function, these reserved places are referenced as vs[0], ..., vs[n-1]. The function list2 above, therefore, should be revised as follows.

        (defCfun ``object list2(x,y) object x,y;'' 1
               ('nil ``vs[0]'')
               ((cons y ``vs[0]'') ``vs[0]'')
               ((cons x ``vs[0]'') ``vs[0]'')
        %      Creturn(vs[0]);
        )

Notice that return is replaced by Creturn. Creturn is similar to return except that Creturn releases the reserved places on return from the function. In the C code within a defCfun form, write ``Creturn(value);'' instead of ``return(value);'', and write ``Cexit;'' instead of ``return;''.

Again, a defCfun form has the above meaning only when it appears as a top-level form in a Lisp source file. Otherwise, the form expands to nil.

KCL converts a Lisp object into a C-language data by using the Common Lisp function coerce: For the C-type int (or char), the object is first coerced to a Lisp integer and the least significant 32-bit (or 8-bit) field is used as the C int (or char). For the C-type float (or double), the object is coerced to a short-float (or a long-float) and this value is used as the C float (or double). Conversion from a C data into a Lisp object is obvious: C char, int, float, and double become the equivalent Lisp character, fixnum, short-float, and long-float, respectively.

Here we list the complete syntax of Clines, defentry, and defCfun macro forms.

Clines-form:
        (Clines { string }* )

defentry-form:
        (defentry function-symbol
                ( { C-type }* )
                { C-function-name | ( { C-type | void } C-function-name ) } )

defCfun-form:
        (defCfun string non-negative-integer
                { string
                  | ( function-symbol { value }* )
                  | (( function-symbol  { value }* ) { place }* ) }  )

value:
place:
         { C-expr | ( C-type C-expr ) }

C-function-name:
C-expr:
         { string | symbol }
 
C-type:
         { object | int | char | float | double }